package com.jbp.service;

import com.alibaba.fastjson.JSON;
import com.jbp.exception.RRException;
import com.jbp.model.WechatConfig;
import com.jbp.utils.UUIDUtil;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.*;

import java.security.spec.PKCS8EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

public class WechatPayUtil {

    public static Map<String, Object> getPayMap(String goodName, String orderNo, int money, String notifyUrl, WechatConfig wechatConfig){
        Map<String, Object> map = new HashMap<>();
        map.put("appId", wechatConfig.getAppid());
        map.put("partnerId", wechatConfig.getMchid());
        String payId = getPayId(goodName, orderNo, money, notifyUrl, wechatConfig);
        map.put("prepayId", payId);
        map.put("packageValue", "Sign=WXPay");
        String nonceStr = UUIDUtil.creatUUID();
        map.put("nonceStr", nonceStr);
        long timeStamp = System.currentTimeMillis() / 1000;
        map.put("timeStamp", timeStamp);
        String msg = wechatConfig.getAppid()+"\n"+timeStamp+"\n"+nonceStr+"\n"+payId+"\n";
        map.put("sign", sign(msg, wechatConfig.getPrivateKey()));
        return map;
    }

    public static String getPayId(String goodName, String orderNo, int money, String notifyUrl, WechatConfig wechatConfig){
        try {
            CloseableHttpClient httpClient = setup(wechatConfig);
            //请求URL
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/app");

            Map<String, Object> amountMap = new HashMap<>();
            amountMap.put("total", money);
            amountMap.put("currency", "CNY");

            Map<String, Object> reqMap = new HashMap<>();
            reqMap.put("mchid", wechatConfig.getMchid());
            reqMap.put("appid", wechatConfig.getAppid());
            reqMap.put("out_trade_no", orderNo);
            reqMap.put("description", goodName);
            reqMap.put("notify_url", notifyUrl);
            reqMap.put("amount", amountMap);
            String json = JSON.toJSONString(reqMap);

            StringEntity entity = new StringEntity(json,"UTF-8");
            entity.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
            entity.setContentType("application/json;charset=utf-8");
            httpPost.setEntity(entity);
            httpPost.setHeader("Accept", "application/json");
            httpPost.addHeader("Content-Type", "application/json;charset=utf-8");
            //完成签名并执行请求
            CloseableHttpResponse response = httpClient.execute(httpPost);

            try {
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == 200) { //处理成功
                    String result = EntityUtils.toString(response.getEntity());
                    System.out.println(result);
                    return JSON.parseObject(result).getString("prepay_id");
                } else if (statusCode == 204) { //处理成功，无返回Body
                    System.out.println("success");
                } else {
                    System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
                    throw new IOException("request failed");
                }
            } finally {
                response.close();
            }
            httpClient.close();
        } catch (Exception e) {
        }
        throw new RRException("支付失败");
    }

    public static CloseableHttpClient setup(WechatConfig wechatConfig) throws Exception {
        // 加载商户私钥（privateKey：私钥字符串）
//        PrivateKey merchantPrivateKey = PemUtil
//                .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));

        PrivateKey merchantPrivateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(wechatConfig.getPrivateKey())));
        // 加载平台证书（mchId：商户号,mchSerialNo：商户证书序列号,apiV3Key：V3密钥）
        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(wechatConfig.getMchid(), new PrivateKeySigner(wechatConfig.getMchSerialNo(), merchantPrivateKey)), wechatConfig.getApiV3Key().getBytes("utf-8"));

        // 初始化httpClient
        return WechatPayHttpClientBuilder.create()
                .withMerchant(wechatConfig.getMchid(), wechatConfig.getMchSerialNo(), merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier)).build();
    }

    public static String sign(String srcData, String privateKey){
        if(srcData==null){
            return "";
        }
        try {
            // 获取证书的私钥
            /*PrivateKey key = PemUtil
                    .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
            String privateKeyString = new String(Base64.encodeBase64((key.getEncoded())));
            System.out.println(privateKeyString);*/

            PrivateKey key = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
            // 进行签名服务
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(key);
            signature.update(srcData.getBytes("UTF-8"));
            byte[] signedData = signature.sign();
            return java.util.Base64.getEncoder().encodeToString(signedData);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    public static String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext,byte[] aesKey)
            throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce);

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);

            return new String(cipher.doFinal(java.util.Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }
}
